package com.xu.system.utils;

import com.xu.base.enums.ResponseCodeEnum;
import com.xu.base.utils.AssertUtils;
import com.xu.base.utils.LocalDateUtils;
import com.xu.system.model.pojo.workTime.WorkTimeConfigBo;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.List;

/**
 * 节假日工具
 *
 * @Author: 许乾伟
 * @Date: 2024/12/24 17:00
 */
public class HolidayUtils {
    /**
     * 周末
     */
    private final List<DayOfWeek> weekends = List.of(DayOfWeek.SUNDAY, DayOfWeek.SATURDAY);
    /**
     * 工作日配置
     */
    private WorkTimeConfigBo config;

    /**
     * 构建一个工时工具类
     * @param config 工作日配置
     */
    public HolidayUtils(WorkTimeConfigBo config) {
        if (config == null) {
            config = new WorkTimeConfigBo();
        }
        this.config = config;
    }

    /**
     * 计算2个时间的有效时间差(秒)
     * @param startTime
     * @param endTime
     * @return
     */
    public Long getWorkTime(LocalDateTime startTime, LocalDateTime endTime) {
        long diff = 0L;
        if (startTime.isAfter(endTime)) {
            return diff;
        }
        while (!isWorking(endTime)) {
            //如果结束时间不是工作日，则转换到前一天的最后一秒
            endTime = endTime.minusDays(1).toLocalDate().atTime(LocalTime.MAX);
        }
        while (true) {
            while (!isWorking(startTime)) {
                //如果开始始时间不是工作日，则加一天
                startTime = startTime.plusDays(1);
            }
            if (startTime.getDayOfYear() == endTime.getDayOfYear()) {
                //同一天的计算
                diff += getDiffSecond(startTime.toLocalTime(), endTime.toLocalTime());
                break;
            }
            diff += getDiffSecond(startTime.toLocalTime(), LocalTime.MAX);
            startTime = startTime.toLocalDate().atTime(LocalTime.MIN).plusDays(1);
        }
        return diff;
    }

    /**
     * 判断传入时间是否上班时间
     * @param time 时间
     * @return true 上班/false 休息
     */
    private boolean isWorking(LocalDateTime time) {
        if (config.getHolidays().contains(time.toLocalDate())) {
            //节假日不上班
            return false;
        }
        if (config.getIsWeekend() && weekends.contains(time.getDayOfWeek()) && !config.getWorkDays().contains(time.toLocalDate())) {
            //开启周末&&当前日是周末&&当前日不是排班日
            return false;
        }
        //默认上班
        return true;
    }

    /**
     * 返回单日2个时间有效时间差(秒)
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return
     */
    private long getDiffSecond(LocalTime startTime, LocalTime endTime) {
        // diff单位：秒
        int diff = 0;
        if (!config.getIsWorkTime()) {
            //不需处理上下班时间和午休时间
            diff = endTime.toSecondOfDay() - startTime.toSecondOfDay();
            if (endTime == LocalTime.MAX) {
                //+1是为了修正跨天少计算的1秒
                diff += 1;
            }
            return diff;
        }
        if (startTime.isAfter(config.getWorkEndTime())) {
            //开始时间在下班时间以后,不计算
            return diff;
        }
        //开始时间切移
        if (startTime.isBefore(config.getWorkStartTime())) {
            startTime = config.getWorkStartTime();
        } else if (config.getIsNoonBreak() && startTime.isAfter(config.getNoonBreakStartTime()) && startTime.isBefore(config.getNoonBreakEndTime())) {
            //开启了午休,并且开始时间在午休时间之间
            startTime = config.getNoonBreakEndTime();
        }
        //结束时间切移
        if (config.getIsNoonBreak() && endTime.isAfter(config.getNoonBreakStartTime()) && endTime.isBefore(config.getNoonBreakEndTime())) {
            endTime = config.getNoonBreakStartTime();
        } else if (endTime.isAfter(config.getWorkEndTime())) {
            endTime = config.getWorkEndTime();
        }
        if (config.getIsNoonBreak() && startTime.compareTo(config.getNoonBreakStartTime()) <= 0 && endTime.compareTo(config.getNoonBreakEndTime()) >= 0) {
            //需要处理午休时间
            diff = config.getNoonBreakEndTime().toSecondOfDay() - config.getNoonBreakStartTime().toSecondOfDay();
        }
        diff = endTime.toSecondOfDay() - startTime.toSecondOfDay() - diff;
        return diff;
    }
}
